/**
 * 
 */
package org.ewaf.framework.struts2.result;

import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspFactory;
import javax.servlet.jsp.PageContext;

import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.VelocityResult;
import org.apache.struts2.views.JspSupportServlet;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * @author peng.sunp
 * 
 */
public class VelocityLayoutResult extends VelocityResult {

	private static final long serialVersionUID = 4034097852975301341L;

	private static final Logger log = Logger.getLogger(VelocityLayoutResult.class);

	public static String KEY_LAYOUT = "layout_template";
	public static String KEY_LAYOUT_CONTENT = "layout_content";

	public static final String PROPERTY_DEFAULT_LAYOUT = "tools.view.servlet.layout.default.template";
	public static final String PROPERTY_LAYOUT_DIR = "tools.view.servlet.layout.directory";
	public static final String PROPERTY_INPUT_ENCODING = "input.encoding";
	public static final String PROPERTY_OUTPUT_ENCODING = "output.encoding";
	public static final String PROPERTY_CONTENT_TYPE = "default.contentType";

	protected VelocityManager velocityManager;
	protected String defaultLayout;
	protected String layoutDir;
	protected String inputEncoding;
	protected String outputEncoding;
	protected String contentType;

	public VelocityLayoutResult() {
		super();
	}

	public VelocityLayoutResult(String location) {
		super(location);
	}

	@Inject
	@Override
	public void setVelocityManager(VelocityManager mgr) {
		super.setVelocityManager(mgr);
		if (mgr != null && velocityManager == null) {
			this.velocityManager = mgr;
			ServletContext servletContext = ServletActionContext.getServletContext();
			velocityManager.init(servletContext);

			defaultLayout = (String) velocityManager.getVelocityEngine().getProperty(PROPERTY_DEFAULT_LAYOUT);
			layoutDir = (String) velocityManager.getVelocityEngine().getProperty(PROPERTY_LAYOUT_DIR);
			if (!layoutDir.endsWith("/")) {
				layoutDir += '/';
			}

			if (!layoutDir.startsWith("/")) {
				layoutDir = "/" + layoutDir;
			}

			// for efficiency's sake, make defaultLayout a full path now
			defaultLayout = layoutDir + defaultLayout;

			inputEncoding = (String) velocityManager.getVelocityEngine().getProperty(PROPERTY_INPUT_ENCODING);
			outputEncoding = (String) velocityManager.getVelocityEngine().getProperty(PROPERTY_OUTPUT_ENCODING);
			contentType = (String) velocityManager.getVelocityEngine().getProperty(PROPERTY_CONTENT_TYPE);

			if (outputEncoding != null && contentType != null) {
				contentType = contentType + ";charset=" + outputEncoding;
			}

		}
	}

	@Override
	protected String getEncoding(String templateLocation) {
		return inputEncoding;
	}

	@Override
	public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
		ValueStack stack = ActionContext.getContext().getValueStack();

		HttpServletRequest request = ServletActionContext.getRequest();
		HttpServletResponse response = ServletActionContext.getResponse();
		JspFactory jspFactory = null;
		Servlet servlet = JspSupportServlet.jspSupportServlet;

		boolean usedJspFactory = false;
		PageContext pageContext = (PageContext) ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT);

		if (pageContext == null && servlet != null) {
			jspFactory = JspFactory.getDefaultFactory();
			pageContext = jspFactory.getPageContext(servlet, request, response, null, true, 8192, true);
			ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, pageContext);
			usedJspFactory = true;
		}

		try {
			//System.err.println("-------------- > finalLocation = " +finalLocation);
			Template t = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, finalLocation, inputEncoding);

			Context context = createContext(velocityManager, stack, request, response, finalLocation);
			// 将页面执行结果写入到字符串缓存中
			StringWriter sw = new StringWriter();
			t.merge(context, sw);
			context.put(KEY_LAYOUT_CONTENT, sw.toString());
			
			Object obj = context.get(KEY_LAYOUT);
			String layout = (obj == null) ? null : obj.toString();
			if (layout == null) {
				// no alternate, use default
				layout = defaultLayout;
			} else {
				// make it a full(er) path
				layout = layoutDir + layout;
			}

			Template layout_vm = null;
		
			try {
				// System.err.println("-------------- > layout=" + layout);
				// load the layout template
				layout_vm = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, layout, inputEncoding);
			} catch (Exception e) {
				velocityManager.getVelocityEngine().error("VelocityLayoutResult: Can't load layout \"" + layout + "\": " + e);

				// if it was an alternate layout we couldn't get...
				if (!layout.equals(defaultLayout)) {
					// try to get the default layout
					// if this also fails, let the exception go
					layout_vm = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, defaultLayout, inputEncoding);
				}
			}

			Writer writer = new OutputStreamWriter(response.getOutputStream(), outputEncoding);
			response.setContentType(contentType);

			layout_vm.merge(context, writer);

			// always flush the writer (we used to only flush it if this was a
			// jspWriter, but someone asked
			// to do it all the time (WW-829). Since Velocity support is being
			// deprecated, we'll oblige :)
			writer.flush();
		} catch (Exception e) {
			log.error("Unable to render Velocity Template, '" + finalLocation + "'", e);
			throw e;
		} finally {
			if (usedJspFactory) {
				jspFactory.releasePageContext(pageContext);
			}
		}

		return;
	}
}
